<template>
  <div class="columns-container" @scroll="containerScroll">
    <div
      v-for="(column, i) in tableColumns"
      :key="i"
      style="display: flex; position: relative;"
      :style="{ order: getAttributeOrder(column.name) }"
    >
      <data-column
        ref="dataColumn"
        :key="`${column.name}`"
        class="column-card"
        :task-id="taskId"
        :tools="selectTools()"
        :column="column"
        :visible="i >= visibleRange[0] && i <= visibleRange[1]"
        :cache="columnQueryStatCache"
        :drag-enabled="!tabBarModeIsNode && isGraphSelfCreate"
        :change-data-type-enabled="tabBarModeIsNode"
        :rename-enabled="tabBarModeIsNode"
        :table-name="tableName"
        :parent-table-name="parentTableName"
        :is-edit="isEditHistory"
        :edit-data="currentHistory"
        :column-filters="columnFilters"
        :column-list="tableColumns"
        :selected-columns="selectedColumns"
        :contextmenu-enable="false"
        :is-visitor="!isGraphSelfCreate"
        :is-selected-label="isLabel(column.name)"
        @config-success="onColumnConfigSuccess"
        @semantic-change="(semantic) => onSemanticChange(column, semantic)"
        @customize-menu-click="(key) => cancelAttribute(key, column.name)"
      >
        <template v-if="isAttr(column.name)" slot="menu">
          <a-menu-item key="cancel-attribute">取消节点属性</a-menu-item>
        </template>
        <template #footer>
          <div v-show="isKeyAttr(column.name)" class="comments">主属性</div>
          <div v-show="isAttr(column.name)" class="comments">节点属性</div>
        </template>
      </data-column>

      <!-- 节点标签 -->
      <div
        v-show="showAddAttributeMask(column.name)"
        class="notAttribute"
        @mouseenter="showAddAttr"
        @mouseleave="hideAddAttr"
        @click="addAsAttribute(column)"
      >
        <div>
          <a-tooltip placement="top" class="tianjia">
            <a-icon-font
              :style="{ 'font-size': '24px', cursor: 'pointer' }"
              type="icontugoujian-tianjiajiedian"
            />
          </a-tooltip>
        </div>
        <div>添加节点属性</div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
import DataColumn from '@/components/studio/data/node-edit-panel/DataColumn.vue'
import GraphViewStore, { IColumn } from '@/store/modules/graphview'
import {
  graphAddCategoryAttribute,
  graphRemoveCategoryAttribute,
  graphAddAction,
  graphUpdateSemantic,
  // tcolumnQueryStat
} from '@/api/visualization'
import { StatisticsAction } from '@/util/data-selection-actions'
import GraphBuild from '@/store/modules/graphbuild'
import { debounce } from 'lodash'
import noPermissionPrompt from '@/components/graph/user-permissions'

enum DragBarMode {
  SOURCEDATA,
  NODEDATA,
}

enum AttributeColumnOrder {
  FIRST,
  SECOND,
  THIRD,
}

@Component({
  components: {
    DataColumn,
  },
})
export default class GraphDataColumn extends Vue {
  @Prop() public tableName: string = ''
  @Prop() taskId!: string

  public parentTableName = ''
  public isEditHistory = false
  public currentHistory = []
  public columnFilters = []
  public selectedColumns = []
  private columnWidth = 180
  private pageSize = 6 // 可视区域可加载个数
  private visibleRange = [0, 0] // 数据列可视范围，[start index，end index]
  private columnQueryStatCache = {}

  // tab bar 模式 是否是节点
  public get tabBarModeIsNode() {
    return GraphViewStore.dragBarMode === DragBarMode.NODEDATA
  }

  // 是否没有编辑权限（图构建创建者）
  public get isGraphSelfCreate() {
    return GraphBuild.isGraphSelfCreate
  }

  /**
   * 修改语义
   */
  public async onSemanticChange(column: any, semantic: any) {
    if (!this.isGraphSelfCreate) {
      noPermissionPrompt()
    } else {
      const data: any = {
        graphId: this.getGraphId(),
        categoryId: GraphBuild.currentCategoryData.id,
        data: {
          col: column.name,
          semantic,
        },
      }
      const response = await graphUpdateSemantic({ data })
      if (response.data.code === 100) {
        this.$emit('change-semantic', response.data.result)
        GraphViewStore.setCurrentCategoryData(response.data.result) // 将新节点存储
        GraphBuild.setCurrentCategoryData(response.data.result) // 将新节点存储
        this.$message.success('操作成功！')
      }
    }
  }

  @Watch('tableName')
  private onTableChange() {
    GraphViewStore.setColumnFilters([])
    GraphViewStore.clearSelectedColumns()
    this.visibleRange = [-1, -1] // 把列都设为不可见，避免列信息更新后不必要的queryStat
    this.columnQueryStatCache = {} // 清空缓存
  }

  @Watch('tableColumns')
  private onTableColumnsChange(newValue: IColumn[]) {
    /**
     * 利用nextTick避免DataColumn.vue 组件里columnChange和visibleChange的重复接口操作
     * 1、columnChange时，visibleRange为[-1,-1]
     * 2、nextTick里visibleChange触发查询
     */
    this.$nextTick(() => {
      //
      if (newValue.length > 0) {
        this.initFetchSetting() // 初始化可见区域范围计算

        if (!this.isEditHistory) {
          this.$el.scrollLeft = 0
        }
      }
    })
  }

  /**
   * 初始化分页加载获取数据相关的配置
   */
  initFetchSetting() {
    const { width: containerWidth } = this.$el.getBoundingClientRect()
    const pageSize = Math.ceil(containerWidth / this.columnWidth) + 1
    this.pageSize =
      pageSize > this.tableColumns.length ? this.tableColumns.length : pageSize
    const startIndex = 0
    // if (this.isEditHistory && this.currentHistory) { // 2021-05-28 图构建部分暂时不支持历史记录再编辑，先注释
    //   const { data } = this.currentHistory
    //   const colName =
    //     data.actionType === StatisticsAction.renameColumn
    //       ? data.newCol
    //       : data.col
    //   const editColumnIndex = this.getColumnIndex(colName)
    //   startIndex = Math.max(editColumnIndex - Math.ceil(this.pageSize / 2), 0)
    //   this.$el.scrollLeft = this.columnWidth * editColumnIndex
    // }
    this.visibleRange = [startIndex, startIndex + this.pageSize]
  }

  private containerScroll = debounce(() => this._handleContainerScroll(), 300)

  /**
   * 容器滚动
   */
  private _handleContainerScroll() {
    const container = this.$el
    const { width } = container.getBoundingClientRect()
    const { scrollLeft } = container
    const pageSize = Math.ceil(width / this.columnWidth)
    const leftInvisibleIndex = Math.floor(scrollLeft / this.columnWidth) - 1
    this.visibleRange = [leftInvisibleIndex, leftInvisibleIndex + pageSize]
  }

  /**
   * 确认
   * 切换数据类型， 重命名， 搜索
   */
  public onColumnConfigSuccess(info: any) {
    if (!info) {
      return
    }
    // 重命名
    if (info.action === 'RENAME') {
      this.columnRenameDebounce(info) // 防抖
      //  类型转换
    } else if (info.action === 'TYPE_TRANSFORM') {
      if (this.isGraphSelfCreate) {
        this.columnRenameDebounce(info)
      } else {
        noPermissionPrompt()
      }
      //  筛选
    } else if (info.action === 'FILTER') {
      this.columnRenameDebounce(info)
    }
  }

  /**
   * 当前选择的 model 节点
   */
  public get currentCategoryData() {
    return GraphViewStore.currentCategoryData
  }

  // 重命名， 修改类型， 筛选 enter 防抖
  private columnRenameDebounce = debounce(
    (info: any) => this.addActionApi(info),
    400
  )

  /**
   * 修改 api
   * @param dataInfo
   */
  public async addActionApi(dataInfo: any) {
    const lastSuccessHistory: any = this.getLastSuccessHistory()
    const data: any = {
      graphId: this.getGraphId(),
      categoryId: GraphBuild.currentCategoryData.id,
      data: {
        ...dataInfo,
        id: lastSuccessHistory.data.id, // 历史 id
        table: lastSuccessHistory.data.table,
      },
    }
    const response = await graphAddAction({ data })
    if (response.data.code === 100) {
      const { result } = response.data
      this.$emit('add-action', result)
      GraphViewStore.setCurrentCategoryData(result.category) // 将新节点存储
      GraphBuild.setCurrentCategoryData(result.category) // 将新节点存储
      if (result.data.status === 'SUCCESS') {
        this.$message.success('操作成功！')
      } else {
        this.$message.error('操作失败，已经定位到上次操作成功数据！')
      }
    }
  }

  /**
   * 获取最后一次成功的历史 ID
   */
  public getLastSuccessHistory() {
    const { historyList } = GraphBuild.currentNodeHistory
    const newHistoryList: any[] = historyList.filter(
      (item: any) => item.data.status === 'SUCCESS'
    )
    return newHistoryList[newHistoryList.length - 1]
  }

  public selectTools(): string[] | null {
    if (GraphViewStore.dragBarMode === DragBarMode.NODEDATA) {
      return [
        StatisticsAction.renameColumn,
        StatisticsAction.filterSelected,
        StatisticsAction.filterNumberRange,
        StatisticsAction.filterDateRange,
        StatisticsAction.filterWildcard,
        StatisticsAction.filterNull,
      ]
    }
    return null
  }

  private cancelAttribute(key: string, attributeName: string) {
    if (key === 'cancel-attribute') {
      let attributeIndex = -1
      let attributeId = ''
      GraphViewStore.currentCategoryData.attrs.forEach(
        (attribute: any, index: number) => {
          if (attribute.column === attributeName) {
            attributeIndex = index
            attributeId = attribute.id
          }
        }
      )

      if (attributeIndex < 0) {
        return
      }

      GraphViewStore.currentCategoryData.attrs.splice(attributeIndex, 1)
      graphRemoveCategoryAttribute({
        data: {
          graphId: this.getGraphId(),
          categoryId: GraphViewStore.currentCategoryData.id,
          attrId: attributeId,
        },
      }).then(() => {
        this.$emit('cancel-attribute', {})
      })
    }
  }

  public getGraphId(): number {
    return Number.parseInt(localStorage.getItem('graphId') || '0', 10)
  }

  private get tableColumns(): IColumn[] {
    return GraphViewStore.tableColumns
  }

  public showAddAttr(mask: any) {
    mask.target.classList.toggle('addAttrBody')
  }

  public hideAddAttr(mask: any) {
    mask.target.classList.toggle('addAttrBody')
  }

  // 添加属性
  public addAsAttribute(column: any) {
    const categoryId = GraphViewStore.currentCategoryData.id
    const attributeIdHashCode = new Date().getTime().toString()
    const currentAttribute: any = {
      column: column.name,
      name: column.name,
      type: column.type,
      id: `${categoryId}_${attributeIdHashCode}`,
    }
    GraphViewStore.currentCategoryData.attrs.push(currentAttribute)

    graphAddCategoryAttribute({
      data: {
        graphId: this.getGraphId(),
        categoryId,
        attr: currentAttribute,
      },
    }).then(() => {
      this.$emit('add-attribute', {
        node: GraphViewStore.currentCategoryData,
        attr: currentAttribute,
      })
    })
  }

  // 排序
  public getAttributeOrder(columnName: string): number {
    if (this.isKeyAttr(columnName)) {
      return AttributeColumnOrder.FIRST
    }

    if (this.isAttr(columnName)) {
      return AttributeColumnOrder.SECOND
    }

    return AttributeColumnOrder.THIRD
  }

  // 是否显示 添加属性的 icon
  public showAddAttributeMask(columnName: string): boolean {
    // 访客权限， 已添加过属性， 是主属性
    if (
      !this.isGraphSelfCreate ||
      this.isKeyAttr(columnName) ||
      this.isAttr(columnName)
    ) {
      return false
    }

    return GraphViewStore.dragBarMode === DragBarMode.NODEDATA
  }

  // 是否是主属性
  public isKeyAttr(columnName: string): boolean {
    // 当前是节点，
    if (GraphViewStore.dragBarMode === DragBarMode.NODEDATA) {
      const { currentCategoryData } = GraphViewStore
      return columnName === currentCategoryData.keyAttr.name
    }
    return false
  }

  // 是否是属性
  public isAttr(columnName: string): boolean {
    if (GraphViewStore.dragBarMode === DragBarMode.NODEDATA) {
      const { currentCategoryData } = GraphViewStore
      let isAttributeResult = false
      currentCategoryData.attrs.forEach((attribute: any) => {
        if (attribute.column === columnName) {
          isAttributeResult = true
        }
      })
      return isAttributeResult
    }
    return false
  }

  // 是否是 label， mask 标签
  public isLabel(columnName: string): boolean {
    if (GraphViewStore.dragBarMode === DragBarMode.NODEDATA) {
      const { currentCategoryData } = GraphViewStore
      let isLabelResult = false
      if (currentCategoryData.originLabel.name === columnName) {
        isLabelResult = true
      }
      return isLabelResult
    }
    return false
  }
}
</script>

<style lang="less" scoped>
.columns-container {
  background: #fafafc;
  // border: 1px solid #e9e9e9;
  border-bottom-width: 0;
  display: flex;
  min-height: 251.5px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 10px;

  &::after {
    content: '';
    order: 100;
    padding-right: 9px;
  }

  &::-webkit-scrollbar {
    height: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(144, 147, 153, 0.5);
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 5px;
  }

  .empty-info {
    flex: 1;
    text-align: center;
  }
}
</style>

<style lang="less" scoped>
.comments {
  background-color: #fff;
  border-top: 1px solid #e9e9e9;
  bottom: 0;
  margin-top: 10px;
  opacity: 0.95;
  padding: 5px;
  position: absolute;
  text-align: center;
  width: 100%;
}

.notAttribute {
  align-items: center;
  background-color: #fff;
  border-radius: 5px;
  bottom: 0;
  color: #6973ff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  left: 0;
  margin: 0 1px;
  opacity: 0.5;
  position: absolute;
  right: 0;
  top: 0;

  .jiahao {
    font-size: 24px;
  }
}

.notAttribute div {
  display: none;
}

.addAttrBody {
  opacity: 0.8;
}

.notAttribute.addAttrBody div {
  display: block;
}

.notAttribute.addAttrBody.tianjia {
  font-size: 24px;
}
</style>
